package com.cy.teducommunitys.com.common.aspect;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Date;

import com.cy.teducommunitys.com.common.annotation.RequiredLog;
import com.cy.teducommunitys.com.common.utils.IPUtils;
import com.cy.teducommunitys.com.cy.entity.Syslog;
import com.cy.teducommunitys.com.cy.entity.User;
import com.cy.teducommunitys.com.cy.service.ISyslogService;
import org.apache.shiro.SecurityUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Service;

import lombok.extern.slf4j.Slf4j;
/**
 *  切面类的构成：
 * 切入点
 *扩展功能（以通知的方式）
 *
 */
@Aspect
@Order
@Service
@Slf4j
public class SysLogAspect {
	/**
	  * @Pointcut注解用于定义切入点
	  * bean表达式为切入点表达式,
	  * bean表达式内部指定的bean对象中
	    *   所有方法为切入点(进行功能扩展的点)
	  */
	//@Pointcut("execution(* com.cy.teducommunitys.com.cy.service.impl..*.*(..))&&!execution(* com.cy.teducommunitys.com.cy.service.impl.BaseServiceImpl..*.*(..))")
	@Pointcut("@annotation(com.cy.teducommunitys.com.common.annotation.RequiredLog)")
	public void logPointCut() {
		
	}
	
	/**
	  * @Around 描述的方法为环绕通知,用于功能增强
	    *   环绕通知(目标方法执行之前和之后都可以执行)
	  * @param jp 连接点 (封装了要执行的目标方法信息)
	  * @return 目标方法的执行结果
	  * @throws Throwable
	  */
	@Around("logPointCut()")
	public Object around(ProceedingJoinPoint jp) throws Throwable {
		long t1 = System.currentTimeMillis();
		log.info("start around:"+t1);
		Object result = jp.proceed(); //调用下一个切面或目标方法
		long t2 = System.currentTimeMillis();
		log.info("end around:"+t2);
		saveObject(jp, (t2-t1));
		return result;		
	}
	
	@Autowired
	ISyslogService svc;
	private void saveObject(ProceedingJoinPoint jp,long time) throws Exception {
		//1.获取要保存的日志信息
		//1.1获取登陆用户(没问题)
		User user=(User) SecurityUtils.getSubject().getPrincipal();
		//1.2获取方法签名(此对象封装了我们要执行的目标方法信息)
		Signature s=jp.getSignature();
		System.out.println(s.getClass().getName());//MethodSignature
		MethodSignature ms=(MethodSignature)s;
		//1.2.1获取目标对象(要执行的业务层对象)
		Class<?> targetClass=jp.getTarget().getClass();
		//1.2.2基于目标业务对象获取要执行的目标方法
		//?思考(为什么要获取此方法呢)
		Method targetMethod=targetClass.getMethod(
				ms.getName(),
				ms.getParameterTypes());
		//1.2.3获取方法上定义的注解内容(定义的操作名)
//		RequiredLog requiredLog=targetMethod.getDeclaredAnnotation(RequiredLog.class);
//		String operation=requiredLog.value();
		//1.2.4获取目标对象方法的全称(类全名+方法名)
		String targetClassName=targetClass.getName();
		String targetMethodName=targetClassName+"."+targetMethod.getName();
		//1.3获取方法执行时的实际参数
		String params= Arrays.toString(jp.getArgs());
		//2.封装日志信息
		Syslog log=new Syslog();
		log.setUserName("admin");
		log.setIp("0.0.0.1");
//		log.setUserName(user.getUserName());
		log.setIp(IPUtils.getIpAddr());
		log.setUrl(targetMethodName);
		//log.setParam(params);
		log.setParam("test");
		log.setActionTime(time);
		log.setResponseCode("200");
		log.setCreatedUser("admin");
		log.setCreatedTime(new Date());
		//3.将日志信息写入到数据库
		svc.saveObject(log);
	}

}
